#include "wakeuper.h"
#include <cstdio>
#if defined(__CYGWIN__)
#    include <unistd.h>
#else
#    include <sys/eventfd.h>
#endif
#include <system_error>
#include <unistd.h>
#include "channel.h"
#include "trace.h"

wakeuper::wakeuper(poll_poller* poller) : channel(poller)
{
    fd_ = wakeup_fd_.fd();
}

wakeuper::EventFd::EventFd()
{
#if defined(__CYGWIN__)
    auto ret = pipe(pipe_fd_);
    if (ret == -1) {
        throw std::system_error(std::error_code(errno, std::system_category()), "pipe");
    }
    event_fd_ = pipe_fd_[0];  // 读事件
#else
    event_fd_ = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC | EFD_SEMAPHORE);
#endif
    if (event_fd_ < 0) {
        throw std::system_error(std::error_code(errno, std::system_category()), "eventfd");
    }
}
wakeuper::EventFd::~EventFd()
{
#if defined(__CYGWIN__)

    close(pipe_fd_[1]);
    close(pipe_fd_[0]);
#else
    close(event_fd_);
#endif
}

ssize_t wakeuper::EventFd::read(uint64_t& flag)
{
    uint64_t num;
    auto     ret = ::read(event_fd_, &num, sizeof(num));
    flag         = num;
    return ret;
}

ssize_t wakeuper::EventFd::write(uint64_t flag)
{
#if defined(__CYGWIN__)
    return ::write(pipe_fd_[1], &flag, sizeof(flag));
#else
    return ::write(event_fd_, &flag, sizeof flag);
#endif
}

wakeuper::~wakeuper()
{
}

void wakeuper::init()
{
    set_read_callback(std::bind(&wakeuper::handle_read, this));
    set_write_callback(std::bind(&wakeuper::handle_write, this));
}

void wakeuper::run_in_loop(std::function<void()> func)
{
    if (!call_funs_) {
        func();
    }
    else {
        pending_funcs_.push_back(std::move(func));
        wakeup();
    }
}

void wakeuper::handle_read()
{
    uint64_t one = 1;
    auto     n   = wakeup_fd_.read(one);
    if (n != sizeof one) {
        TRACE_SYSTEM("wakeup::read failed");
    }
    call_funs_ = true;
    for (const auto& pending_func : pending_funcs_) {
        pending_func();
    }
    call_funs_ = false;
}

void wakeuper::handle_write()
{
    TRACE_SYSTEM("wakeup::handle_write");
}

void wakeuper::wakeup()
{
    uint64_t one = 1;
    ssize_t  n   = wakeup_fd_.write(one);
    if (n != sizeof one) {
        TRACE_SYSTEM("wakeup::write failed");
    }
}